package com.google.code.mircle.core.transfers;

import java.util.ArrayList;
import java.util.Calendar;
import java.util.Date;

import org.apache.commons.lang3.time.DateUtils;

import com.google.code.mircle.core.Core;
import com.google.code.mircle.core.Worker;
import com.google.code.mircle.core.Worker.DownloadState;
import com.google.code.mircle.core.Worker.Status;

public class Transfer {

    Core core;
    Worker worker;
    TransfersPlugin plugin;

    Thread thread;

    ArrayList<Listener> list = new ArrayList<Transfer.Listener>();

    public static interface Listener {
        public void start(Transfer t);

        public void changed(Transfer t);

        public void stop(Transfer t);
    }

    public static interface LongRun {
        public void run(Runnable notify);
    }

    public Transfer(Core core, TransfersPlugin p, Worker w) {
        this.core = core;
        this.worker = w;
        this.plugin = p;
    }

    public void addListener(Listener l) {
        if (l == null)
            throw new NullPointerException();

        list.add(l);
    }

    public void removeListener(Listener l) {
        list.remove(l);
    }

    public String toString() {
        Status s = worker.status();
        String str = "";
        if (s.delay != 0) {
            return s.title + " retyring in " + s.delay;
        } else {
            if (worker.getDownloadState() == DownloadState.DOWNLOAD_QUEUED)
                str += "Q ";

            if (s.total != null) {
                float progress = s.current / (float) s.total;
                str += (int) (Math.floor(progress * 100)) + "%";
            }

            if (getWorker().getDownloadState() == DownloadState.DOWNLOAD_START) {
                float speed = s.downloadRate;
                if (speed < 1000000) {
                    speed /= 1024;
                    str += " / " + String.format("%.02f", speed) + " KB/s";
                } else {
                    speed /= 1024 * 1024;
                    str += " / " + String.format("%.02f", speed) + " MB/s";
                }

                if (s.downloadRateAverage > 0) {
                    long diff = ((s.total - s.current) / s.downloadRateAverage);
                    long days = diff / (60 * 60 * 24);
                    diff -= days * (60 * 60 * 24);
                    long hours = diff / (60 * 60);
                    diff -= hours * (60 * 60);
                    long min = diff / 60;
                    diff -= min * 60;
                    long sec = diff;
                    str += " / ";
                    if (days != 0)
                        str += "days:" + days + " ";
                    if (hours != 0)
                        str += String.format("%02d:", hours);
                    if (hours != 0 || min != 0)
                        str += String.format("%02d:", min);

                    // 00:00:12
                    if (hours == 0 && min == 0 && sec > 9)
                        str += String.format("%02d", sec);
                    // 00:00:09
                    else if (hours == 0 && min == 0 && sec < 10)
                        str += String.format("%d", sec);
                    // 02:04:09
                    else
                        str += String.format("%02d", sec);

                    if (days == 0 && hours == 0 && min == 0)
                        str += " secs";
                }
            }

            str += " / " + s.title;

            return str;
        }
    }

    public void stop() {
        worker.stop();
    }

    public void close() {
        if (!isAlive())
            return;

        worker.close();

        try {
            thread.join();
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
    }

    public void start() {
        worker.start();

        longRun(new LongRun() {
            @Override
            public void run(Runnable notify) {
                worker.run(notify);
            }
        });
    }

    public void longRun(final LongRun r) {
        if (thread != null && thread.isAlive())
            throw new RuntimeException("already started");

        final Transfer that = this;

        thread = new Thread(new Runnable() {
            @Override
            public void run() {
                Core.invoke(new Runnable() {
                    @Override
                    public void run() {
                        for (Listener l : list) {
                            l.changed(that);
                            l.start(that);
                        }
                    }
                });

                r.run(new Runnable() {
                    @Override
                    public void run() {
                        Core.invoke(new Runnable() {
                            @Override
                            public void run() {
                                for (Listener l : list)
                                    l.changed(that);
                            }
                        });
                    }
                });

                // send changed event after this thread done (download complete)
                Core.invoke(new Runnable() {
                    @Override
                    public void run() {
                        try {
                            thread.join();
                        } catch (Exception e) {
                            throw new RuntimeException(e);
                        }
                        for (Listener l : list) {
                            l.changed(that);
                            l.stop(that);
                        }
                    }
                });
            }
        }, "Long run thread " + worker.getSource());
        thread.start();
    }

    public Worker getWorker() {
        return worker;
    }

    public boolean isAlive() {
        if (thread == null)
            return false;

        return thread.isAlive();
    }
}
